#include "DXUT.h"
#include <PrecompiledHeader.h>
#include "Buffer.h"
using namespace d3d11;

// TODO - add something to allow adding of data later for buffers if 
// created with just space allocated (initData == NULL)

//-----------------------------------------------------------------------------------------------
// Create Vertex Buffer 11
//-----------------------------------------------------------------------------------------------
Buffer::Buffer(D3D11_USAGE usage, uint uiStride, uint uiDataSize, void *initData )
{
	m_pD3DDevice9 = NULL;
	m_pD3DDevice11 = DXUTGetD3D11Device();
	m_pBuffer = NULL;
	m_pIndexBuffer9 = NULL;
	m_pVertexBuffer9 = NULL;
	m_dataSize		= uiDataSize;
	m_stride		= uiStride;
	m_indexFormat11 = DXGI_FORMAT_UNKNOWN;
	m_uiIndexCount	= 0;
	m_type = Buffer::D3D11VERTEX;

	D3D11_BUFFER_DESC bufferDesc;
	bufferDesc.Usage            = usage;
	bufferDesc.ByteWidth        = uiDataSize;
	bufferDesc.BindFlags        = D3D11_BIND_VERTEX_BUFFER;
	bufferDesc.CPUAccessFlags   = 0;
	bufferDesc.MiscFlags        = 0;

	HRESULT hr = S_OK;

	if(initData == NULL)
	{
		if(FAILED(hr = m_pD3DDevice11->CreateBuffer(&bufferDesc, NULL, &m_pBuffer)))
		{
			CASSERT(m_pBuffer != NULL, "CreateBuffer failed.");
		}
	}
	else if (uiDataSize == 0)
	{
		// handle empty buffers
		return;
	}
	else
	{
		D3D11_SUBRESOURCE_DATA srInitData;
		srInitData.pSysMem = initData;
		srInitData.SysMemPitch = 0;
		srInitData.SysMemSlicePitch = 0;

		if(FAILED(hr = m_pD3DDevice11->CreateBuffer(&bufferDesc, &srInitData, &m_pBuffer)))
		{
			CASSERT(m_pBuffer != NULL, "CreateBuffer failed.");
		}
	}
}

//-----------------------------------------------------------------------------------------------
// Create Index Buffer 11
//-----------------------------------------------------------------------------------------------
Buffer::Buffer(D3D11_USAGE usage, uint uiDataSize, DXGI_FORMAT index_format, UINT uiCount, void *initData )
{
	m_pD3DDevice11 = DXUTGetD3D11Device();
	m_pD3DDevice9 = NULL;
	m_pBuffer = NULL;
	m_pIndexBuffer9 = NULL;
	m_pVertexBuffer9 = NULL;
	m_dataSize		= uiDataSize;
	m_stride		= 0;
	m_indexFormat11 = index_format;
	m_uiIndexCount	= uiCount;
	m_type = Buffer::D3D11INDEX;

	D3D11_BUFFER_DESC bufferDesc;
	bufferDesc.Usage            = usage;
	bufferDesc.ByteWidth        = uiDataSize;
	bufferDesc.BindFlags        = D3D11_BIND_INDEX_BUFFER;
	bufferDesc.CPUAccessFlags   = 0;
	bufferDesc.MiscFlags        = 0;

	HRESULT hr = S_OK;

	if(initData == NULL)
	{
		if(FAILED(hr = m_pD3DDevice11->CreateBuffer(&bufferDesc, NULL, &m_pBuffer)))
		{
			CASSERT(m_pBuffer != NULL, "CreateBuffer failed.");
		}
	}
	else if( uiDataSize == 0 )
	{
		// handle zero sized buffers
		return;
	}
	else
	{
		D3D11_SUBRESOURCE_DATA InitData;
		InitData.pSysMem = initData;
		InitData.SysMemPitch = 0;
		InitData.SysMemSlicePitch = 0;

		if(FAILED(hr = m_pD3DDevice11->CreateBuffer(&bufferDesc, &InitData, &m_pBuffer)))
		{
			CASSERT(m_pBuffer != NULL, "CreateBuffer failed.");
		}
	}
}

//-----------------------------------------------------------------------------------------------
// Create Vertex Buffer 9
//-----------------------------------------------------------------------------------------------
Buffer::Buffer(D3DFORMAT index_format, DWORD usage, D3DPOOL pool, uint uiIndexCount, uint uiDataSize, void *initData)
{
	m_pD3DDevice9 = DXUTGetD3D9Device();
	m_pD3DDevice11 = NULL;
	m_pBuffer = NULL;
	m_pIndexBuffer9 = NULL;
	m_pVertexBuffer9 = NULL;
	m_type = Buffer::D3D9INDEX;
	m_stride = 0;
	m_dataSize = uiDataSize;
	m_indexformat = index_format;
	m_uiIndexCount = uiIndexCount;

	if( uiDataSize == 0 )
	{
		return;
	}

	HRESULT hr = S_OK;

	if(FAILED(hr = m_pD3DDevice9->CreateIndexBuffer(uiDataSize, 
													usage, 
													index_format, 
													pool, 
													&m_pIndexBuffer9, 
													NULL)))
	{
		CASSERT(m_pIndexBuffer9 != NULL, "CreateBuffer failed. (CreateIndexBuffer9)");
	}

	if (initData != NULL)
	{
		// To fill the index buffer, you must lock the buffer to gain 
		// access to the indices. This mechanism is required because index
		// buffers may be in device memory.

		void* pIndices;

		hr = S_OK;

		hr =  m_pIndexBuffer9->Lock( 
			  0,					// Fill from start of the buffer
			  uiDataSize,		// Size of the data to load
			  (void**)&pIndices,	// Returned index data
			  0 );					// Send default flags to the lock

		if (hr != S_OK)
		{
			SAFE_RELEASE(m_pIndexBuffer9);
			CASSERT(hr == S_OK, "Cannot Lock Index Buffer 9.");
		}

		memcpy( pIndices, initData, uiDataSize );
		m_pIndexBuffer9->Unlock();
	}
}

//-----------------------------------------------------------------------------------------------
// Create Index Buffer 9
//-----------------------------------------------------------------------------------------------
Buffer::Buffer(DWORD vertex_format, DWORD usage, D3DPOOL pool, uint stride, uint uiDataSize, void *initData)
{
	m_pD3DDevice9 = DXUTGetD3D9Device();
	m_pD3DDevice11 = NULL;
	m_pBuffer = NULL;
	m_pIndexBuffer9 = NULL;
	m_pVertexBuffer9 = NULL;
	m_type = Buffer::D3D9VERTEX;
	m_stride = stride;
	m_dataSize = uiDataSize;
	m_fvf = vertex_format;
	m_indexformat = D3DFMT_UNKNOWN;

	if( uiDataSize == 0 )
	{
		return;
	}

	HRESULT hr = S_OK;
	
	if(FAILED(hr = m_pD3DDevice9->CreateVertexBuffer(uiDataSize,
														usage,
														vertex_format,
														pool,
														&m_pVertexBuffer9,
														NULL)))
	{
		CASSERT(m_pVertexBuffer9 != NULL, "CreateBuffer failed. (VertexBuffer9)");
	}

	if (initData != NULL)
	{
		// To fill the index buffer, you must lock the buffer to gain 
		// access to the indices. This mechanism is required because index
		// buffers may be in device memory.

		void* pIndices;

		hr = S_OK;

		hr =  m_pVertexBuffer9->Lock( 
			  0,					// Fill from start of the buffer
			  uiDataSize,		// Size of the data to load
			  (void**)&pIndices,	// Returned index data
			  0 );					// Send default flags to the lock

		if (hr != S_OK)
		{
			SAFE_RELEASE(m_pVertexBuffer9);
			CASSERT(hr == S_OK, "Cannot Lock Vertex Buffer9.");
		}

		memcpy( pIndices, initData, uiDataSize);
		m_pVertexBuffer9->Unlock();
	}
}

//-----------------------------------------------------------------------------------------------
// Create Constant Buffer 11
//-----------------------------------------------------------------------------------------------
Buffer::Buffer(D3D11_USAGE usage, uint uiDataSize)
{
	m_pD3DDevice11 = DXUTGetD3D11Device();
	m_pD3DDevice9 = NULL;
	m_pBuffer = NULL;
	m_pIndexBuffer9 = NULL;
	m_pVertexBuffer9 = NULL;
	m_dataSize		= uiDataSize;
	m_stride		= 0;
	m_indexFormat11 = DXGI_FORMAT_UNKNOWN;
	m_uiIndexCount	= 0;
	m_type = Buffer::D3D11CONSTANT;

	D3D11_BUFFER_DESC bufferDesc;
	bufferDesc.Usage            = usage;
	bufferDesc.ByteWidth        = uiDataSize;
	bufferDesc.BindFlags        = D3D11_BIND_CONSTANT_BUFFER;
	bufferDesc.CPUAccessFlags   = 0;
	bufferDesc.MiscFlags        = 0;

	HRESULT hr = S_OK;

	if( uiDataSize == 0 )
	{
		return;
	}

	if(FAILED(hr = m_pD3DDevice11->CreateBuffer(&bufferDesc, NULL, &m_pBuffer)))
	{
		CASSERT(m_pBuffer != NULL, "CreateBuffer failed.");
	}
}

Buffer::~Buffer()
{
	SAFE_RELEASE(m_pBuffer);
	SAFE_RELEASE(m_pIndexBuffer9);
	SAFE_RELEASE(m_pVertexBuffer9);
}

void Buffer::SetAsVertexBuffer()
{
	CASSERT(m_type == Buffer::D3D11VERTEX || m_type == Buffer::D3D9VERTEX, "Buffer isn't a Vertex Buffer.");

	if (m_pD3DDevice9 == NULL)
	{
		// set directX 11 vertex buffer (all of it) as vertex buffer - D3D9Device could not be obtained
		uint offset = 0;	
		DXUTGetD3D11DeviceContext()->IASetVertexBuffers(0, 1, &m_pBuffer, &m_stride, &offset);	
	}
	else
	{
		// must be DirectX 9.
		m_pD3DDevice9->SetStreamSource( 0, m_pVertexBuffer9, 0, m_stride );
		// set vertex format - maybe keep this in separate function, but fvf is tied into the vertexes being used to render.
		m_pD3DDevice9->SetFVF(m_fvf);
	}
}

void Buffer::SetAsIndexBuffer()
{
	CASSERT(m_type == Buffer::D3D11INDEX || m_type == Buffer::D3D9INDEX, "Buffer isn't a Index Buffer.");

	if (m_pD3DDevice9 == NULL)
	{
		uint offset = 0;	
		DXUTGetD3D11DeviceContext()->IASetIndexBuffer(m_pBuffer, m_indexFormat11, offset);	
	}
	else
	{
		m_pD3DDevice9->SetIndices( m_pIndexBuffer9 );
	}
}
